/**
 * 通用工具函数库
 */

// ============ 类型工具 ============

// 检查值是否为空
export const isEmpty = (value: any): boolean => {
  if (value === null || value === undefined) return true
  if (typeof value === 'string') return value.trim() === ''
  if (Array.isArray(value)) return value.length === 0
  if (typeof value === 'object') return Object.keys(value).length === 0
  return false
}

// 检查是否为有效邮箱
export const isValidEmail = (email: string): boolean => {
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
  return emailRegex.test(email)
}

// 检查是否为有效手机号
export const isValidPhone = (phone: string): boolean => {
  const phoneRegex = /^1[3-9]\d{9}$/
  return phoneRegex.test(phone)
}

// 检查是否为有效身份证号
export const isValidIdCard = (idCard: string): boolean => {
  const idCardRegex = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/
  return idCardRegex.test(idCard)
}

// 检查是否为有效URL
export const isValidUrl = (url: string): boolean => {
  try {
    new URL(url)
    return true
  } catch {
    return false
  }
}

// ============ 字符串工具 ============

// 首字母大写
export const capitalize = (str: string): string => {
  if (!str) return str
  return str.charAt(0).toUpperCase() + str.slice(1)
}

// 驼峰转换
export const camelCase = (str: string): string => {
  return str.replace(/[-_\s]+(.)?/g, (_, char) => char ? char.toUpperCase() : '')
}

// 短横线转换
export const kebabCase = (str: string): string => {
  return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase()
}

// 下划线转换
export const snakeCase = (str: string): string => {
  return str.replace(/([a-z])([A-Z])/g, '$1_$2').toLowerCase()
}

// 截断字符串
export const truncate = (str: string, length: number, suffix = '...'): string => {
  if (str.length <= length) return str
  return str.slice(0, length) + suffix
}

// 移除HTML标签
export const stripHtml = (html: string): string => {
  return html.replace(/<[^>]*>/g, '')
}

// 转义HTML
export const escapeHtml = (text: string): string => {
  const div = document.createElement('div')
  div.textContent = text
  return div.innerHTML
}

// ============ 数字工具 ============

// 格式化数字
export const formatNumber = (num: number, decimals = 2): string => {
  return num.toLocaleString('zh-CN', {
    minimumFractionDigits: decimals,
    maximumFractionDigits: decimals
  })
}

// 格式化货币
export const formatCurrency = (amount: number, currency = 'CNY'): string => {
  return new Intl.NumberFormat('zh-CN', {
    style: 'currency',
    currency
  }).format(amount)
}

// 格式化百分比
export const formatPercent = (value: number, decimals = 2): string => {
  return new Intl.NumberFormat('zh-CN', {
    style: 'percent',
    minimumFractionDigits: decimals,
    maximumFractionDigits: decimals
  }).format(value)
}

// 数字范围限制
export const clamp = (num: number, min: number, max: number): number => {
  return Math.min(Math.max(num, min), max)
}

// 生成随机数
export const random = (min: number, max: number): number => {
  return Math.floor(Math.random() * (max - min + 1)) + min
}

// ============ 日期时间工具 ============

// 格式化日期
export const formatDate = (date: Date | string | number, format = 'YYYY-MM-DD'): string => {
  const d = new Date(date)
  const year = d.getFullYear()
  const month = String(d.getMonth() + 1).padStart(2, '0')
  const day = String(d.getDate()).padStart(2, '0')
  const hour = String(d.getHours()).padStart(2, '0')
  const minute = String(d.getMinutes()).padStart(2, '0')
  const second = String(d.getSeconds()).padStart(2, '0')

  return format
    .replace('YYYY', String(year))
    .replace('MM', month)
    .replace('DD', day)
    .replace('HH', hour)
    .replace('mm', minute)
    .replace('ss', second)
}

// 格式化相对时间
export const formatRelativeTime = (date: Date | string | number): string => {
  const now = new Date()
  const target = new Date(date)
  const diffMs = now.getTime() - target.getTime()
  const diffSec = Math.floor(diffMs / 1000)
  const diffMin = Math.floor(diffSec / 60)
  const diffHour = Math.floor(diffMin / 60)
  const diffDay = Math.floor(diffHour / 24)

  if (diffSec < 60) return '刚刚'
  if (diffMin < 60) return `${diffMin}分钟前`
  if (diffHour < 24) return `${diffHour}小时前`
  if (diffDay < 7) return `${diffDay}天前`
  
  return new Intl.DateTimeFormat('zh-CN').format(target)
}

// 获取时间戳
export const getTimestamp = (): number => {
  return Date.now()
}

// 日期加减
export const addDays = (date: Date, days: number): Date => {
  const result = new Date(date)
  result.setDate(result.getDate() + days)
  return result
}

// ============ 数组工具 ============

// 数组去重
export const unique = <T>(arr: T[]): T[] => {
  return [...new Set(arr)]
}

// 数组分组
export const groupBy = <T>(arr: T[], key: keyof T): Record<string, T[]> => {
  return arr.reduce((groups, item) => {
    const groupKey = String(item[key])
    if (!groups[groupKey]) {
      groups[groupKey] = []
    }
    groups[groupKey].push(item)
    return groups
  }, {} as Record<string, T[]>)
}

// 数组排序
export const sortBy = <T>(arr: T[], key: keyof T, order: 'asc' | 'desc' = 'asc'): T[] => {
  return [...arr].sort((a, b) => {
    const aVal = a[key]
    const bVal = b[key]
    if (aVal < bVal) return order === 'asc' ? -1 : 1
    if (aVal > bVal) return order === 'asc' ? 1 : -1
    return 0
  })
}

// 数组分页
export const paginate = <T>(arr: T[], page: number, size: number): T[] => {
  const start = (page - 1) * size
  return arr.slice(start, start + size)
}

// 数组随机打乱
export const shuffle = <T>(arr: T[]): T[] => {
  const result = [...arr]
  for (let i = result.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [result[i]!, result[j]!] = [result[j]!, result[i]!]
  }
  return result
}

// ============ 对象工具 ============

// 深拷贝
export const deepClone = <T>(obj: T): T => {
  if (obj === null || typeof obj !== 'object') return obj
  if (obj instanceof Date) return new Date(obj.getTime()) as T
  if (obj instanceof Array) return obj.map(item => deepClone(item)) as T
  if (typeof obj === 'object') {
    const cloned = {} as T
    Object.keys(obj).forEach(key => {
      (cloned as any)[key] = deepClone((obj as any)[key])
    })
    return cloned
  }
  return obj
}

// 对象合并
export const merge = <T extends Record<string, any>>(target: T, ...sources: Partial<T>[]): T => {
  const result = { ...target }
  sources.forEach(source => {
    Object.keys(source).forEach(key => {
      if (source[key] !== undefined) {
        (result as any)[key] = source[key]
      }
    })
  })
  return result
}

// 获取嵌套属性
export const get = <T = any>(obj: any, path: string, defaultValue?: T): T | undefined => {
  const keys = path.split('.')
  let result = obj
  
  for (const key of keys) {
    if (result && typeof result === 'object' && key in result) {
      result = result[key]
    } else {
      return defaultValue
    }
  }
  
  return result !== undefined ? result : defaultValue
}

// 设置嵌套属性
export const set = (obj: any, path: string, value: any): void => {
  const keys = path.split('.')
  const lastKey = keys.pop()
  
  if (!lastKey) return
  
  let target = obj
  for (const key of keys) {
    if (!target[key] || typeof target[key] !== 'object') {
      target[key] = {}
    }
    target = target[key]
  }
  
  target[lastKey] = value
}

// 删除嵌套属性
export const unset = (obj: any, path: string): boolean => {
  const keys = path.split('.')
  const lastKey = keys.pop()
  
  if (!lastKey) return false
  
  let target = obj
  for (const key of keys) {
    if (!target[key] || typeof target[key] !== 'object') {
      return false
    }
    target = target[key]
  }
  
  if (lastKey in target) {
    delete target[lastKey]
    return true
  }
  
  return false
}

// 检查是否有嵌套属性
export const has = (obj: any, path: string): boolean => {
  const keys = path.split('.')
  let target = obj
  
  for (const key of keys) {
    if (!target || typeof target !== 'object' || !(key in target)) {
      return false
    }
    target = target[key]
  }
  
  return true
}

// ============ URL工具 ============

// 解析查询参数
export const parseQuery = (search: string): Record<string, string> => {
  const params = new URLSearchParams(search.startsWith('?') ? search.slice(1) : search)
  const result: Record<string, string> = {}
  
  for (const [key, value] of params.entries()) {
    result[key] = value
  }
  
  return result
}

// 构建查询参数
export const buildQuery = (params: Record<string, any>): string => {
  const searchParams = new URLSearchParams()
  
  Object.entries(params).forEach(([key, value]) => {
    if (value !== null && value !== undefined) {
      searchParams.append(key, String(value))
    }
  })
  
  return searchParams.toString()
}

// 获取文件扩展名
export const getFileExtension = (filename: string): string => {
  const lastDot = filename.lastIndexOf('.')
  return lastDot !== -1 ? filename.slice(lastDot + 1) : ''
}

// ============ 函数工具 ============

// 防抖
export const debounce = <T extends (...args: any[]) => any>(
  func: T,
  wait: number
): (...args: Parameters<T>) => void => {
  let timeout: NodeJS.Timeout | null = null
  
  return (...args: Parameters<T>) => {
    if (timeout) clearTimeout(timeout)
    timeout = setTimeout(() => func(...args), wait)
  }
}

// 节流
export const throttle = <T extends (...args: any[]) => any>(
  func: T,
  wait: number
): (...args: Parameters<T>) => void => {
  let lastTime = 0
  
  return (...args: Parameters<T>) => {
    const now = Date.now()
    if (now - lastTime >= wait) {
      lastTime = now
      func(...args)
    }
  }
}

// 重试函数
export const retry = async <T>(
  fn: () => Promise<T>,
  maxAttempts = 3,
  delay = 1000
): Promise<T> => {
  let lastError: Error
  
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    try {
      return await fn()
    } catch (error) {
      lastError = error as Error
      if (attempt < maxAttempts) {
        await new Promise(resolve => setTimeout(resolve, delay))
      }
    }
  }
  
  throw lastError!
}

// ============ 浏览器工具 ============

// 复制到剪贴板
export const copyToClipboard = async (text: string): Promise<boolean> => {
  try {
    if (navigator.clipboard) {
      await navigator.clipboard.writeText(text)
      return true
    } else {
      // 降级方案
      const textArea = document.createElement('textarea')
      textArea.value = text
      document.body.appendChild(textArea)
      textArea.select()
      document.execCommand('copy')
      document.body.removeChild(textArea)
      return true
    }
  } catch {
    return false
  }
}

// 获取设备信息
export const getDeviceInfo = () => {
  const userAgent = navigator.userAgent
  return {
    isMobile: /Mobile|Android|iPhone|iPad/.test(userAgent),
    isIOS: /iPhone|iPad|iPod/.test(userAgent),
    isAndroid: /Android/.test(userAgent),
    isChrome: /Chrome/.test(userAgent),
    isFirefox: /Firefox/.test(userAgent),
    isSafari: /Safari/.test(userAgent) && !/Chrome/.test(userAgent)
  }
}

// 获取浏览器存储大小
export const getStorageSize = (storage: Storage): number => {
  let total = 0
  for (const key in storage) {
    if (storage.hasOwnProperty(key)) {
      total += storage[key].length + key.length
    }
  }
  return total
}

// 下载文件
export const downloadFile = (data: string | Blob, filename: string, type = 'text/plain'): void => {
  const blob = typeof data === 'string' ? new Blob([data], { type }) : data
  const url = URL.createObjectURL(blob)
  const link = document.createElement('a')
  link.href = url
  link.download = filename
  document.body.appendChild(link)
  link.click()
  document.body.removeChild(link)
  URL.revokeObjectURL(url)
}

// ============ 导出所有工具 ============

export const utils = {
  // 类型工具
  isEmpty,
  isValidEmail,
  isValidPhone,
  isValidIdCard,
  isValidUrl,
  
  // 字符串工具
  capitalize,
  camelCase,
  kebabCase,
  snakeCase,
  truncate,
  stripHtml,
  escapeHtml,
  
  // 数字工具
  formatNumber,
  formatCurrency,
  formatPercent,
  clamp,
  random,
  
  // 日期时间工具
  formatDate,
  formatRelativeTime,
  getTimestamp,
  addDays,
  
  // 数组工具
  unique,
  groupBy,
  sortBy,
  paginate,
  shuffle,
  
  // 对象工具
  deepClone,
  merge,
  get,
  set,
  unset,
  has,
  
  // URL工具
  parseQuery,
  buildQuery,
  getFileExtension,
  
  // 函数工具
  debounce,
  throttle,
  retry,
  
  // 浏览器工具
  copyToClipboard,
  getDeviceInfo,
  getStorageSize,
  downloadFile
}

export default utils